package com.immediatus.graphics.managers;

import com.immediatus.graphics.contracts.ITexture;

import javax.microedition.khronos.opengles.GL10;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;


public class TextureManager{
    private final HashSet<ITexture> _texturesManaged = new HashSet<ITexture>();
    private final ArrayList<ITexture> _texturesLoaded = new ArrayList<ITexture>();
    private final ArrayList<ITexture> _texturesToBeLoaded = new ArrayList<ITexture>();
    private final ArrayList<ITexture> _texturesToBeUnloaded = new ArrayList<ITexture>();

    protected synchronized void clear(){
        this._texturesToBeLoaded.clear();
        this._texturesLoaded.clear();
        this._texturesManaged.clear();
    }

    public synchronized boolean loadTexture(final ITexture texture_){
        if (this._texturesManaged.contains(texture_)){
            this._texturesToBeUnloaded.remove(texture_);
            return false;
        } else{
            this._texturesManaged.add(texture_);
            this._texturesToBeLoaded.add(texture_);
            return true;
        }
    }

    public synchronized boolean unloadTexture(final ITexture texture_){
        if (this._texturesManaged.contains(texture_)){
            if (this._texturesLoaded.contains(texture_)){
                this._texturesToBeUnloaded.add(texture_);
            } else if (this._texturesToBeLoaded.remove(texture_)){
                this._texturesManaged.remove(texture_);
            }
            return true;
        } else{
            return false;
        }
    }

    public void loadTextures(final ITexture... textures_){
        for (int i = textures_.length - 1; i >= 0; i--){
            this.loadTexture(textures_[i]);
        }
    }

    public void unloadTextures(final ITexture... textures_){
        for (int i = textures_.length - 1; i >= 0; i--){
            this.unloadTexture(textures_[i]);
        }
    }

    public synchronized void reloadTextures(){
        final HashSet<ITexture> managedTextures = this._texturesManaged;
        for (final ITexture texture : managedTextures){
            texture.setLoadedToHardware(false);
        }

        this._texturesToBeLoaded.addAll(this._texturesLoaded);
        this._texturesLoaded.clear();

        this._texturesManaged.removeAll(this._texturesToBeUnloaded);
        this._texturesToBeUnloaded.clear();
    }

    public synchronized void updateTextures(final GL10 gl_){
        final HashSet<ITexture> texturesManaged = this._texturesManaged;
        final ArrayList<ITexture> texturesLoaded = this._texturesLoaded;
        final ArrayList<ITexture> texturesToBeLoaded = this._texturesToBeLoaded;
        final ArrayList<ITexture> texturesToBeUnloaded = this._texturesToBeUnloaded;

        final int textursLoadedCount = texturesLoaded.size();

        if (textursLoadedCount > 0){
            for (int i = textursLoadedCount - 1; i >= 0; i--){
                final ITexture textureToBeReloaded = texturesLoaded.get(i);
                if (textureToBeReloaded.isUpdateOnHardwareNeeded()){
                    try{
                        textureToBeReloaded.reloadToHardware(gl_);
                    } catch (IOException e){
                    }
                }
            }
        }

        final int texturesToBeLoadedCount = texturesToBeLoaded.size();

        if (texturesToBeLoadedCount > 0){
            for (int i = texturesToBeLoadedCount - 1; i >= 0; i--){
                final ITexture textureToBeLoaded = texturesToBeLoaded.remove(i);
                if (!textureToBeLoaded.isLoadedToHardware()){
                    try{
                        textureToBeLoaded.loadToHardware(gl_);
                    } catch (IOException e){
                    }
                }
                texturesLoaded.add(textureToBeLoaded);
            }
        }

        final int texturesToBeUnloadedCount = texturesToBeUnloaded.size();

        if (texturesToBeUnloadedCount > 0){
            for (int i = texturesToBeUnloadedCount - 1; i >= 0; i--){
                final ITexture textureToBeUnloaded = texturesToBeUnloaded.remove(i);
                if (textureToBeUnloaded.isLoadedToHardware()){
                    textureToBeUnloaded.unloadFromHardware(gl_);
                }
                texturesLoaded.remove(textureToBeUnloaded);
                texturesManaged.remove(textureToBeUnloaded);
            }
        }
        if (texturesToBeLoadedCount > 0 || texturesToBeUnloadedCount > 0){
            System.gc();
        }
    }
}
